package com.plian.system.service.fr.indicators.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.plian.Tools.BigDecimalUtil;
import com.plian.Tools.DateUtil;
import com.plian.Tools.StringPool;
import com.plian.Tools.operation.OperationExpressionUtil;
import com.plian.system.dto.fr.indicators.IndicatorsValueDTO;
import com.plian.system.entity.fr.indicators.ComplexIndicatorsValue;
import com.plian.system.entity.sys.Org;
import com.plian.system.mapper.fr.indicators.ComplexIndicatorsValueMapper;
import com.plian.system.mapper.fr.indicators.IndicatorsValueMapper;
import com.plian.system.properties.GzwProperties;
import com.plian.system.service.fr.indicators.IComplexIndicatorsValueService;
import com.plian.system.service.fr.indicatorsvalue.IGraphicOrgRelationService;
import com.plian.system.service.sys.orgzation.IOrgService;
import com.plian.system.vo.fr.indicatorsValue.IndicatorsValueVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.plian.system.constant.fr.IndicatorsCodeConstant.*;
import static java.util.stream.Collectors.reducing;

@Slf4j
@Service
@AllArgsConstructor
public class ComplexIndicatorsValueServiceImpl extends ServiceImpl<ComplexIndicatorsValueMapper, ComplexIndicatorsValue> implements IComplexIndicatorsValueService {

    private IndicatorsValueMapper indicatorsValueMapper;

    private IOrgService orgService;

    private IGraphicOrgRelationService graphicOrgRelationService;

    private GzwProperties gzwProperties;

    private final static String MAX_MONTH = "12";

    @Override
    public void init() {
        int nowYear = Integer.valueOf(DateUtil.format(DateUtil.now(), DateUtil.PATTERN_YEAR));
        for (int year = 2019; year <= nowYear; year++) {
            for (int month = 1; month <= 12; month++) {
                updateByYearAndMonth(year, month);
            }
        }
        int nowMonth = Integer.valueOf(DateUtil.format(DateUtil.now(), DateUtil.PATTERN_ONLY_MONTH));
        for (int month = 1; month <= nowMonth; month++) {
            updateByYearAndMonth(nowYear, month);
        }
    }

    @Override
    public void updateByYearAndMonth(int year, int month) {
        String reportTime = year + StringPool.DASH + (month < 10 ? "0" + month : month);
        List<String> groupIds = orgService.getGzwUnits(orgService.getDTFlag(reportTime))
                .stream().map(Org::getId).collect(Collectors.toList());
        //直接监管合并节点数据
        //净资产收益率
        List<ComplexIndicatorsValue> list = new ArrayList<>();
        list.add(ComplexIndicatorsValue.builder().value(getNetAssets(groupIds, reportTime))
                .orgId(gzwProperties.getReportId())
                .year(year)
                .month(month)
                .indicatorsCode(JZCSYL)
                .indicatorsName(JZCSYL_NAME)
                .createTime(DateUtil.now()).build());
        //归母净资产收益率
        list.add(ComplexIndicatorsValue.builder().value(getParentNetAssets(groupIds, reportTime))
                .orgId(gzwProperties.getReportId())
                .year(year)
                .month(month)
                .indicatorsCode(GMJZCSYL)
                .indicatorsName(GMJZCSYL_NAME)
                .createTime(DateUtil.now()).build());
        remove(Wrappers.<ComplexIndicatorsValue>lambdaQuery().eq(ComplexIndicatorsValue::getYear, year)
                .eq(ComplexIndicatorsValue::getMonth, month));
        saveBatch(list);
    }

    @Override
    public BigDecimal getComplexIndicatorsValue(String orgId, String indicatorsCode, int year, int month) {
        ComplexIndicatorsValue complexIndicatorsValue = getOne(Wrappers.<ComplexIndicatorsValue>lambdaQuery()
                .eq(ComplexIndicatorsValue::getIndicatorsCode, JZCSYL)
                .eq(ComplexIndicatorsValue::getOrgId,  gzwProperties.getReportId())
                .eq(ComplexIndicatorsValue::getYear, year)
                .eq(ComplexIndicatorsValue::getMonth, month));
        if (complexIndicatorsValue == null){
            return null;
        }
        return complexIndicatorsValue.getValue();
    }

    @Override
    public BigDecimal getComplexIndicatorsValue(String orgId, String indicatorsCode, String reportTime) {
        return getComplexIndicatorsValue(orgId, indicatorsCode, Integer.parseInt(reportTime.split(StringPool.DASH)[0]),
                Integer.parseInt(reportTime.split(StringPool.DASH)[1]));
    }

    // 归母净资产收益率
    private BigDecimal getParentNetAssets(List<String> groupIds, String reportTime) {
        String formula = "KLJ21*200*(12/month)/(K37+KLAST37)";
        IndicatorsValueDTO indicatorsValueDTO = new IndicatorsValueDTO();
        indicatorsValueDTO.setOrgIds(groupIds);
        String[] reportTimes = reportTime.split(StringPool.DASH);
        int year = Integer.parseInt(reportTimes[0]);
        indicatorsValueDTO.setYear(reportTimes[0]);
        indicatorsValueDTO.setMonth(reportTimes[1]);
        indicatorsValueDTO.setCodes(Stream.of(KLJ21, K37, KLAST37).collect(Collectors.toList()));
        List<IndicatorsValueVO> valueVOS = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
        Map<String, BigDecimal> codeValueMap = valueVOS.stream()
                .collect(Collectors.groupingBy(IndicatorsValueVO::getCode, reducing(BigDecimal.ZERO, IndicatorsValueVO::getValue, BigDecimal::add)));

        List<String> subList = graphicOrgRelationService.getSubtractChildList(reportTime);
        List<String> addList = graphicOrgRelationService.getAddChildList(reportTime);
        if (year > 2019){
            indicatorsValueDTO.setOrgIds(subList);
            List<IndicatorsValueVO> subValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
            Map<String, BigDecimal> subCodeValueMap = subValueVos.stream()
                    .collect(Collectors.groupingBy(IndicatorsValueVO::getCode, reducing(BigDecimal.ZERO, IndicatorsValueVO::getValue, BigDecimal::add)));
            indicatorsValueDTO.setOrgIds(addList);
            List<IndicatorsValueVO> addValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
            Map<String, BigDecimal> addCodeValueMap = addValueVos.stream()
                    .collect(Collectors.groupingBy(IndicatorsValueVO::getCode, reducing(BigDecimal.ZERO, IndicatorsValueVO::getValue, BigDecimal::add)));
            codeValueMap.forEach((k,v) -> {
                if (subCodeValueMap.containsKey(k)){
                    v = v.subtract(subCodeValueMap.get(k));
                }
                if (addCodeValueMap.containsKey(k)){
                    v = v.add(addCodeValueMap.get(k));
                }
                codeValueMap.put(k, v);
            });
        }
        if (year < 2022){
            formula = "KLASTLJ21*200*(12/month)/(K37+KLAST37)";
            indicatorsValueDTO.setOrgIds(groupIds);
            indicatorsValueDTO.setYear(String.valueOf(year + 1));
            indicatorsValueDTO.setCodes(Stream.of(KLASTLJ21).collect(Collectors.toList()));
            List<IndicatorsValueVO> kLastLj21 = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
            BigDecimal kLastLj21decimal = kLastLj21.stream().map(IndicatorsValueVO::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (year + 1 > 2019){
                indicatorsValueDTO.setOrgIds(subList);
                List<IndicatorsValueVO> subValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
                BigDecimal subValue = subValueVos.stream().map(IndicatorsValueVO::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
                indicatorsValueDTO.setOrgIds(addList);
                List<IndicatorsValueVO> addValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
                BigDecimal addValue = addValueVos.stream().map(IndicatorsValueVO::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
                kLastLj21decimal = BigDecimalUtil.add(BigDecimalUtil.subtract(kLastLj21decimal, subValue), addValue);
            }
            codeValueMap.put(KLASTLJ21, kLastLj21decimal);
        }
        OperationExpressionUtil operationExpressionUtil = new OperationExpressionUtil();
        codeValueMap.put("month", new BigDecimal(reportTimes[1]));
        return new BigDecimal(operationExpressionUtil.operation(formula, codeValueMap, 6, BigDecimal.ROUND_HALF_UP));
    }

    //净资产收益率
    private BigDecimal getNetAssets(List<String> groupIds, String reportTime) {
        String formula = "K58*200*(12/month)/(K36+KLAST36)";
        IndicatorsValueDTO indicatorsValueDTO = new IndicatorsValueDTO();
        indicatorsValueDTO.setOrgIds(groupIds);
        String[] reportTimes = reportTime.split(StringPool.DASH);
        int year = Integer.parseInt(reportTimes[0]);
        indicatorsValueDTO.setYear(reportTimes[0]);
        indicatorsValueDTO.setMonth(reportTimes[1]);
        indicatorsValueDTO.setCodes(Stream.of(K58, K36).collect(Collectors.toList()));
        List<IndicatorsValueVO> valueVOS = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
        Map<String, BigDecimal> codeValueMap = valueVOS.stream()
                .collect(Collectors.groupingBy(IndicatorsValueVO::getCode, reducing(BigDecimal.ZERO, IndicatorsValueVO::getValue, BigDecimal::add)));

        List<String> subList = graphicOrgRelationService.getSubtractChildList(reportTime);
        List<String> addList = graphicOrgRelationService.getAddChildList(reportTime);
        if (year > 2019){
            indicatorsValueDTO.setOrgIds(subList);
            List<IndicatorsValueVO> subValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
            Map<String, BigDecimal> subCodeValueMap = subValueVos.stream()
                    .collect(Collectors.groupingBy(IndicatorsValueVO::getCode, reducing(BigDecimal.ZERO, IndicatorsValueVO::getValue, BigDecimal::add)));
            indicatorsValueDTO.setOrgIds(addList);
            List<IndicatorsValueVO> addValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
            Map<String, BigDecimal> addCodeValueMap = addValueVos.stream()
                    .collect(Collectors.groupingBy(IndicatorsValueVO::getCode, reducing(BigDecimal.ZERO, IndicatorsValueVO::getValue, BigDecimal::add)));
            codeValueMap.forEach((k,v) -> {
                if (subCodeValueMap.containsKey(k)){
                    v = v.subtract(subCodeValueMap.get(k));
                }
                if (addCodeValueMap.containsKey(k)){
                    v = v.add(addCodeValueMap.get(k));
                }
                codeValueMap.put(k, v);
            });
        }
        int lastYear = year - 1;
        indicatorsValueDTO.setCodes(Stream.of(K36).collect(Collectors.toList()));
        indicatorsValueDTO.setYear(String.valueOf(lastYear));
        indicatorsValueDTO.setMonth(MAX_MONTH);
        indicatorsValueDTO.setOrgIds(groupIds);
        List<IndicatorsValueVO> last36List = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
        BigDecimal last36 = last36List.stream().map(IndicatorsValueVO::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (lastYear > 2019){
            indicatorsValueDTO.setOrgIds(subList);
            List<IndicatorsValueVO> subValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
            BigDecimal subValue = subValueVos.stream().map(IndicatorsValueVO::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
            indicatorsValueDTO.setOrgIds(addList);
            List<IndicatorsValueVO> addValueVos = indicatorsValueMapper.queryGroupValueByCode(indicatorsValueDTO);
            BigDecimal addValue = addValueVos.stream().map(IndicatorsValueVO::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
            last36 = BigDecimalUtil.add(BigDecimalUtil.subtract(last36, subValue), addValue);
        }
        codeValueMap.put(KLAST36, last36);
        OperationExpressionUtil operationExpressionUtil = new OperationExpressionUtil();
        codeValueMap.put("month", new BigDecimal(reportTimes[1]));
        return new BigDecimal(operationExpressionUtil.operation(formula, codeValueMap, 6, BigDecimal.ROUND_HALF_UP));
    }
}
